#ifndef B2D_TEXTUREATLAS_H
#define B2D_TEXTUREATLAS_H

#include "ResourceFactory.h"
#include "Maths.h"
#include "Rect.h"
#include "IGraphicsDevice.h"

NS_B2D_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	 Texture Atlas
/// 		 This holds a collection of texture sections.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API TextureAtlas final
	: public BaseResource
{
	B2D_RESOURCE

private:
	struct Item
	{
		string name;
		vec2 origin;
		Rectu pixelRegion;
	};

	typedef std::vector<Item> ItemCollection;

	TexturePtr mTextures[TextureUnit::MAX];
	ItemCollection mItems;

	VertexBuffer* mVertexBuffer;
	IndexBuffer* mIndexBuffer;
	bool mBuffersDirty;

public:
	TextureAtlas(void);
	~TextureAtlas(void);

	// ::IResource
	size_t getSizeEstimate(void) const;
	void load(const StreamPtr& stream);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Initialises the atlas with the given texture. Any regions defined will be lost.
	///
	/// \param	texture	The texture.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void initWithTexture(const TexturePtr& texture);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Switch texture without losing the region data.
	///
	/// \param	texture	The texture.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void switchTexture(const TexturePtr& texture);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the texture used by this atlas.
	///
	/// \return	The texture.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const TexturePtr& getTexture(void) const;


	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	(Re)Generates the data buffers.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void generateBuffers(void);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if the buffers used by this texture atlas need updating.
	///
	/// \return	true if it needs updating, false if up to date.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	bool buffersDirty(void) const { return mBuffersDirty; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets vertex buffer containing the vertices for all the sprites. Having one vertex buffer
	/// 		is more friendly than a separate one for each sprite.
	///
	/// \return	The vertex buffer.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	VertexBuffer* getVertexBuffer(void) const { return mVertexBuffer; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets index buffer.
	///
	/// \return	The index buffer.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	IndexBuffer* getIndexBuffer(void) const { return mIndexBuffer; }


	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Adds an item to the atlas using the parameters provided.
	///
	/// \param	name  	The name.
	/// \param	region	The region in pixels.
	/// \param	origin	The relative origin of the sprite (default = center{0.5,0.5}).
	///
	/// \return	The index of the new item.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	size_t addItem(const string& name, const Rectu& region, const vec2& origin = vec2(0.5f,0.5f));
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the index of item with given name if it exists.
	///
	/// \param	name	The name of the item.
	///
	/// \return	-1 if not found, the index otherwise.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	int indexOf(const string& name) const;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Draws the item (sprite) at given index.
	///
	/// \param [in,out]	device	The graphics device.
	/// \param	index		  	The index od the region to render.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void render(IGraphicsDevice& device, size_t index) const;


private:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Applies the textures to the graphics device
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void applyTextures(IGraphicsDevice& g) const;
};

typedef std::shared_ptr<TextureAtlas> TextureAtlasPtr;
typedef DefaultResourceFactory<TextureAtlas,TextureAtlas> TextureAtlasFactory;

NS_B2D_END

#endif